//customer.cc

#include "cineplex.h"

extern int NUM_CUSTOMERS;

Customer::Customer(char* name, int group, IterList* underlings)
{
	myName = name;
	msgList = new MsgList();
	brQueue = new IterList();
	msgLock = new Lock(myName);
	sleepCon = new Condition(myName);
	groupNumber = group;
	
	repliesWaiting = 0;
	msgsToSend = 0;
	numPopcorn = 0;
	numSoda = 0;
	myRow = -1;
	myCol = -1;
	
	subs = underlings;
	if(subs)
	{
		state = "JoinTixLine";
		numSubs = subs->getSize();
		msgsToSend = numSubs;
		money = 100000000000.0f; //since this doesn't mean anything yet...
	}else
	{
		state = "sNewSub";
		numSubs = 0;
		money = 0;
	}
}

Customer::~Customer()
{

}

void
Customer::Start()
{
	cinema = getCineplex();
	if(!cinema->testPattern)
	{
		printf("Customer %s in Group %d has entered the movie theater\n", myName, groupNumber);
		if(getCineplex()->runSchedulers) Scheduler();
	}else{
		cinema = getCineplex();
		//printf("Customer %s in Group %d running test pattern %d\n", myName, groupNumber, cinema->testPattern);
		if(cinema->testPattern == 1) CT1();
		else if(cinema->testPattern == 3) CT3();
		else if(cinema->testPattern == 4) CT4();
	}
}

void Customer::CT1()
{
	//printf("CT1\n");
	Scheduler();
	currentThread->Yield();
	cinema->testAgentsLeft = cinema->testAgentsLeft - 1;
}

void Customer::CT3()
{
	//printf("CT1\n");
	Scheduler();
	currentThread->Yield();
	Scheduler();
	//currentThread->Yield();//there's a yield in the action here
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	//currentThread->Yield();
	cinema->testAgentsLeft = cinema->testAgentsLeft - 1;
}

void Customer::CT4()
{
	//currentThread->Yield();
	//currentThread->Yield();
	Scheduler();
	Scheduler();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	//cinema->testAgentsLeft = cinema->testAgentsLeft - 1;
}

void
Customer::SendMessage(char* name, int argc, ...)
{
	msgLock->Acquire();
	
	IterList* args = new IterList();
	va_list vali;
	va_start(vali, argc);
	
	for ( int i = 0; i < argc; i++ )        
    {
        args->Append(va_arg ( vali, void* )); 
    }
	va_end(vali);
	
	msgList->Append(name, args);
	sleepCon->Signal(msgLock);
	msgLock->Release();
}

void
Customer::SendMessage(char* name, IterList* args)
{
	msgLock->Acquire();
	
	msgList->Append(name, args);
	sleepCon->Signal(msgLock);
	
	msgLock->Release();
}

void
Customer::Scheduler()
{
	int runScheduler = getCineplex()->runSchedulers;
	
	do {
		//LEADER
		if(!strcmp(state,"JoinTixLine"))
		{
			JoinTixLine();
		}else if(!strcmp(state,"WaitTixLine"))
		{
			WaitTixLine();
		}else if(!strcmp(state,"CheckNewTixLine"))
		{
			CheckNewTixLine();
		}else if(!strcmp(state,"RecTixClerk"))
		{
			RecTixClerk();
		}else if(!strcmp(state,"TixOrderPlaced"))
		{
			TixOrderPlaced();
		}else if(!strcmp(state,"TixPaySent"))
		{
			TixPaySent();
		}else if(!strcmp(state,"AskSubsFood"))
		{
			AskSubsFood();
		}else if(!strcmp(state,"WaitSubFood"))
		{
			WaitSubFood();
		}else if(!strcmp(state,"JoinFoodLine"))
		{
			JoinFoodLine();
		}else if(!strcmp(state,"WaitFoodLine"))
		{
			WaitFoodLine();
		}else if(!strcmp(state,"CheckNewFoodLine"))
		{
			CheckNewFoodLine();
		}else if(!strcmp(state,"RecFoodClerk"))
		{
			RecFoodClerk();
		}else if(!strcmp(state,"PopcornOrderPlaced"))
		{
			PopcornOrderPlaced();
		}else if(!strcmp(state,"DrinkOrderPlaced"))
		{
			DrinkOrderPlaced();
		}else if(!strcmp(state,"FoodPaySent"))
		{
			FoodPaySent();
		}else if(!strcmp(state,"JoinEntryLine"))
		{
			JoinEntryLine();
		}else if(!strcmp(state,"WaitEntryLine"))
		{
			WaitEntryLine();
		}else if(!strcmp(state,"CheckNewEntryLine"))
		{
			CheckNewEntryLine();
		}else if(!strcmp(state,"RecTixTaker"))
		{
			RecTixTaker();
		}else if(!strcmp(state,"GaveTix"))
		{
			GaveTix();
		}else if(!strcmp(state,"TellSubsGoIn"))
		{
			TellSubsGoIn();
		}else if(!strcmp(state,"LeadGoIn"))
		{
			LeadGoIn();
		}else if(!strcmp(state,"WatchingMovie"))
		{
			WatchingMovie();
		}else if(!strcmp(state,"LeadRegroup"))
		{
			LeadRegroup();
		}else if(!strcmp(state,"AskUseBR"))
		{
			AskUseBR();
		}else if(!strcmp(state,"FindOutBR"))
		{
			FindOutBR();
		}else if(!strcmp(state,"WaitBR"))
		{
			WaitBR();
		}else if(!strcmp(state,"LeaveCineplex"))
		{
			LeaveCineplex();
		}else if(!strcmp(state,"GoToLobby"))
		{
			GoToLobby();
		}else if(!strcmp(state,"InLobby"))
		{
			InLobby();
		}else if(!strcmp(state,"RecSizeRequest"))
		{
			RecSizeRequest();
		}
		
		//SUBS
		else if(!strcmp(state,"sNewSub"))
		{
			sNewSub();
		}else if(!strcmp(state,"sSendFoodOrder"))
		{
			sSendFoodOrder();
		}else if(!strcmp(state,"sFoodOrderSent"))
		{
			sFoodOrderSent();
		}else if(!strcmp(state,"sRecGoIn"))
		{
			sRecGoIn();
		}else if(!strcmp(state,"sWaitForSeat"))
		{
			sWaitForSeat();
		}else if(!strcmp(state,"sWatchingMovie"))
		{
			sWatchingMovie();
		}else if(!strcmp(state,"sRegroup"))
		{
			sRegroup();
		}else if(!strcmp(state,"sLeaveTheater"))
		{
			sLeaveTheater();
		}else if(!strcmp(state,"sAskedBR"))
		{
			sAskedBR();
		}else if(!strcmp(state,"sUseBR"))
		{
			sUseBR();
		}else if(!strcmp(state,"sWaitBR"))
		{
			sWaitBR();
		}else if(!strcmp(state,"sLeaveCineplex"))
		{
			sLeaveCineplex();
		}
		
		//terminate thread
		else if(!strcmp(state,"Completed"))
		{
			cinema->customersRunning = cinema->customersRunning - 1;
			runScheduler = 0;
		}
	} while(runScheduler);
}

//LEADER ACTIONS
//The manager is coded in such a way that he will never close all lines
void Customer::JoinTixLine()
{
	cinema->tixLinesLock->Acquire();
	int lineLengths[MAX_TIX_CLERKS];
	for(int i = 0; i < cinema->tixLines->getSize(); i++)
	{
		//lineLengths->Append((void*)&(((Line*)cinema->tixLines->Get(i))->getSize())); //length of line i //HOLY GOD WHAT WAS I THINKING
		lineLengths[i] = ((Line*)cinema->tixLines->Get(i))->getSize();
	}
	int shortest = -1;
	int shortLen = NUM_CUSTOMERS;
	for(int i = 0; i < NUM_TIX_CLERKS; i++)
	{
		int currLen = lineLengths[i];
		if(currLen == -1) continue;
		if(currLen < shortLen)
		{
			shortLen = currLen;
			shortest = i;
		}
	}
	
	currentLine = (Line*)cinema->tixLines->Get(shortest);
	currentLine->Enqueue(this);
	printf("Customer %s in Group %d is getting in TicketClerk line %d\n", myName, groupNumber, shortest);
	state = "WaitTixLine";
	
	cinema->tixLinesLock->Release();
}

void Customer::WaitTixLine()
{
	//if this line closed, join a new one
	if(!currentLine->isOpen)
	{
		state = "JoinTixLine";
	}else{
		msgLock->Acquire();
		IterList* clerkMsg = msgList->Find("ComeBuyTix");
		if(clerkMsg)
		{
			replyTo = clerkMsg->Get(0);
			state = "RecTixClerk";
		}
		else state = "CheckNewTixLine";
		msgLock->Release();
	}
	currentThread->Yield();
}

void Customer::CheckNewTixLine()
{
 	cinema->tixLinesLock->Acquire();
	int lineLengths[MAX_TIX_CLERKS];
	for(int i = 0; i < cinema->tixLines->getSize(); i++)
	{
		//lineLengths->Append((void*)&(((Line*)cinema->tixLines->Get(i))->getSize())); //length of line i //HOLY GOD WHAT WAS I THINKING
		lineLengths[i] = ((Line*)cinema->tixLines->Get(i))->getSize();
	}
	int shortest = -1;
	int shortLen = currentLine->getSize();
	for(int i = 0; i < NUM_TIX_CLERKS; i++)
	{
		int currLen = lineLengths[i];
		if(currLen == -1) continue;
		if(shortest == -1) //haven't found line shorter than own 
		{
			if(currLen < shortLen - 1) //at least two shorter, or position wouldn't improve
			{
				shortLen = currLen;
				shortest = i;
			}
		}else{
			if(currLen < shortLen) //already sufficiently shorter than own line
			{
				shortLen = currLen;
				shortest = i;
			}
		}
	}
	
	if(shortest != -1)
	{
		currentLine->Dequeue(this);
		currentLine = (Line*)cinema->tixLines->Get(shortest);
		printf("Customer %s in Group %d is moving to TicketClerk line %d\n", myName, groupNumber, shortest);
		currentLine->Enqueue(this);
	}
	
	state = "WaitTixLine";
	
	cinema->tixLinesLock->Release();
}

void Customer::RecTixClerk()
{
	printf("Customer %s in Group %d is walking up to %s to buy %d tickets\n", myName, groupNumber, ((TicketClerk*)replyTo)->myName, numSubs+1);
	msgInt = numSubs+1;
	((TicketClerk*)replyTo)->SendMessage("OrderTix", 2, this, &msgInt);///&tix);
	state = "TixOrderPlaced";
}

void Customer::TixOrderPlaced()
{
	msgLock->Acquire();
	IterList* clerkMsg = msgList->Find("PayMeForTix");
	if(clerkMsg)
	{
		msgFloat = clerkMsg->GetFloat(1);
		money -= msgFloat; //handle failures later
		printf("Customer %s in Group %d is paying %s $%f for tickets\n", myName, groupNumber, ((TicketClerk*)replyTo)->myName, msgFloat);
		((TicketClerk*)replyTo)->SendMessage("HereIsTixMoney", 2,this, &msgFloat);
		state = "TixPaySent";
		msgLock->Release();
	}else{
		msgLock->Release();
		
		sleepCon->Wait(msgLock);
	}
}

void Customer::TixPaySent()
{
	msgLock->Acquire();
	IterList* clerkMsg = msgList->Find("HereAreTickets");
	if(clerkMsg)
	{
		tickets = (Ticket*)clerkMsg->Get(0);
		msgsToSend = numSubs;
		numPopcorn = ((getRandPercent() < 75) ? 1 : 0); //decide own popcorn
		numSoda = ((getRandPercent() < 75) ? 1 : 0); //decide own soda
		printf("Customer %s in Group %d is leaving %s\n", myName, groupNumber, ((TicketClerk*)replyTo)->myName);
		state = "AskSubsFood";
		msgLock->Release();
	}else{
		msgLock->Release();
		
		sleepCon->Wait(msgLock);
	}
}

void Customer::AskSubsFood()
{
	if(msgsToSend == 0)
	{
		repliesWaiting = numSubs;
		state = "WaitSubFood";
	}else{
		replyTo = subs->Get(msgsToSend-1);
		((Customer*)replyTo)->SendMessage("WouldYouLikeFood",1,this);
		msgsToSend = msgsToSend - 1;
	}
}

void Customer::WaitSubFood()
{
	if(repliesWaiting == 0)
	{
		if(!numSoda && !numPopcorn) state = "JoinEntryLine";
		else state = "JoinFoodLine";
	}else{
		msgLock->Acquire();
		IterList* foodMsg = msgList->Find("IWouldLike");
		if(foodMsg)
		{
			numPopcorn = numPopcorn + foodMsg->GetInt(0);
			numSoda = numSoda + foodMsg->GetInt(1);
			msgLock->Release();
			repliesWaiting--;
		}else{
			msgLock->Release();
			sleepCon->Wait(msgLock);
		}
	}
}

void Customer::JoinFoodLine()
{
	cinema->foodLinesLock->Acquire();
	int lineLengths[MAX_FOOD_CLERKS];
	for(int i = 0; i < cinema->foodLines->getSize(); i++)
	{
		lineLengths[i] = ((Line*)cinema->foodLines->Get(i))->getSize();
	}
	int shortest = -1;
	int shortLen = NUM_CUSTOMERS;
	for(int i = 0; i < NUM_FOOD_CLERKS; i++)
	{
		int currLen = lineLengths[i];
		if(currLen == -1) continue;
		if(currLen < shortLen)
		{
			shortLen = currLen;
			shortest = i;
		}
	}
	
	currentLine = (Line*)cinema->foodLines->Get(shortest);
	currentLine->Enqueue(this);
	printf("Customer %s in Group %d is getting in FoodClerk line %d\n", myName, groupNumber, shortest);
	state = "WaitFoodLine";
	
	cinema->foodLinesLock->Release();
}

void Customer::WaitFoodLine()
{
	//if this line closed, join a new one
	if(!currentLine->isOpen)
	{
		state = "JoinFoodLine";
	}else{
		msgLock->Acquire();
		IterList* clerkMsg = msgList->Find("ComeBuyFood");
		if(clerkMsg)
		{
			replyTo = clerkMsg->Get(0);
			state = "RecFoodClerk";
		}
		else state = "CheckNewFoodLine";
		msgLock->Release();
	}
		currentThread->Yield();
}

void Customer::CheckNewFoodLine()
{
	cinema->foodLinesLock->Acquire();
	int lineLengths[MAX_FOOD_CLERKS];
	for(int i = 0; i < cinema->foodLines->getSize(); i++)
	{
		lineLengths[i] = ((Line*)cinema->foodLines->Get(i))->getSize();
	}
	int shortest = -1;
	int shortLen = currentLine->getSize();
	for(int i = 0; i < NUM_FOOD_CLERKS; i++)
	{
		int currLen = lineLengths[i];
		if(currLen == -1) continue;
		if(shortest == -1) //haven't found line shorter than own 
		{
			if(currLen < shortLen - 1) //at least two shorter, or position wouldn't improve
			{
				shortLen = currLen;
				shortest = i;
			}
		}else{
			if(currLen < shortLen) //already sufficiently shorter than own line
			{
				shortLen = currLen;
				shortest = i;
			}
		}
	}
	
	if(shortest != -1)
	{
		currentLine->Dequeue(this);
		currentLine = (Line*)cinema->foodLines->Get(shortest);
		printf("Customer %s in Group %d is moving to FoodClerk line %d\n", myName, groupNumber, shortest);
		currentLine->Enqueue(this);
	}
	
	state = "WaitFoodLine";
	
	cinema->foodLinesLock->Release();
}

void Customer::RecFoodClerk()
{
	printf("Customer %s in Group %d is walking up to %s to buy %d popcorns and %d sodas\n", myName, groupNumber, ((FoodClerk*)replyTo)->myName, numPopcorn, numSoda);
	((FoodClerk*)replyTo)->SendMessage("WantPopcorn", 2, this, &numPopcorn);
	state = "PopcornOrderPlaced";
}

void Customer::PopcornOrderPlaced()
{
	msgLock->Acquire();
	IterList* clerkMsg = msgList->Find("AnythingElse");
	if(clerkMsg)
	{
		((TicketClerk*)replyTo)->SendMessage("WantSoda", 2, this, &numSoda);
		state = "DrinkOrderPlaced";
		msgLock->Release();
	}else{
		msgLock->Release();
		
		sleepCon->Wait(msgLock);
	}
}

void Customer::DrinkOrderPlaced()
{
	msgLock->Acquire();
	IterList* clerkMsg = msgList->Find("PayMeForFood");
	if(clerkMsg)
	{
		float cost = clerkMsg->GetFloat(1);
		money -= cost; //handle failures later
		printf("Customer %s in Group %d is paying %s $%f for food\n", myName, groupNumber, ((FoodClerk*)replyTo)->myName, cost);
		((FoodClerk*)replyTo)->SendMessage("HereIsFoodMoney", 2,this, &cost);
		state = "FoodPaySent";
		msgLock->Release();
	}else{
		msgLock->Release();
		
		sleepCon->Wait(msgLock);
	}
}

void Customer::FoodPaySent()
{
	msgLock->Acquire();
	IterList* clerkMsg = msgList->Find("HereIsYourFood");
	if(clerkMsg)
	{
		popcorns = (Popcorn*)clerkMsg->Get(0);
		sodas = (Soda*)clerkMsg->Get(1);
		printf("Customer %s in Group %d is leaving %s\n", myName, groupNumber, ((FoodClerk*)replyTo)->myName);
		state = "JoinEntryLine";
		msgLock->Release();
	}else{
		msgLock->Release();
		
		sleepCon->Wait(msgLock);
	}
}

void Customer::JoinEntryLine()
{
	cinema->entryLinesLock->Acquire();
	int lineLengths[MAX_TIX_TAKERS];
	for(int i = 0; i < cinema->entryLines->getSize(); i++)
	{
		lineLengths[i] = ((Line*)cinema->entryLines->Get(i))->getSize();
	}
	int shortest = -1;
	int shortLen = NUM_CUSTOMERS;
	for(int i = 0; i < NUM_TIX_TAKERS; i++)
	{
		int currLen = lineLengths[i];
		if(currLen == -1) continue;
		if(currLen < shortLen)
		{
			shortLen = currLen;
			shortest = i;
		}
	}
	
	currentLine = (Line*)cinema->entryLines->Get(shortest);
	currentLine->Enqueue(this);
	printf("Customer %s in Group %d is getting in TicketTaker line %d\n", myName, groupNumber, shortest);
	state = "WaitEntryLine";
	
	cinema->entryLinesLock->Release();
}

void Customer::WaitEntryLine()
{
	//if this line closed, join a new one
	if(!currentLine->isOpen)
	{
		state = "JoinEntryLine";
	}else{
		msgLock->Acquire();
		IterList* clerkMsg = msgList->Find("GoToTicketTaker");
		if(clerkMsg)
		{
			replyTo = clerkMsg->Get(0);
			state = "RecTixTaker";
		}
		else state = "CheckNewEntryLine";
		msgLock->Release();
	}
		currentThread->Yield();
}

void Customer::CheckNewEntryLine()
{
	cinema->entryLinesLock->Acquire();
	int lineLengths[MAX_TIX_TAKERS];
	for(int i = 0; i < cinema->entryLines->getSize(); i++)
	{
		lineLengths[i] = ((Line*)cinema->entryLines->Get(i))->getSize();
	}
	int shortest = -1;
	int shortLen = currentLine->getSize();
	for(int i = 0; i < NUM_TIX_TAKERS; i++)
	{
		int currLen = lineLengths[i];
		if(currLen == -1) continue;
		if(shortest == -1) //haven't found line shorter than own 
		{
			if(currLen < shortLen - 1) //at least two shorter, or position wouldn't improve
			{
				shortLen = currLen;
				shortest = i;
			}
		}else{
			if(currLen < shortLen) //already sufficiently shorter than own line
			{
				shortLen = currLen;
				shortest = i;
			}
		}
	}
	
	if(shortest != -1)
	{
		currentLine->Dequeue(this);
		currentLine = (Line*)cinema->entryLines->Get(shortest);
		printf("Customer %s in Group %d is moving to TicketTaker line %d\n", myName, groupNumber, shortest);
		currentLine->Enqueue(this);
	}
	
	state = "WaitEntryLine";
	
	cinema->entryLinesLock->Release();
}

void Customer::RecTixTaker()
{
	printf("Customer %s in Group %d is walking up to %s to give %d tickets\n", myName, groupNumber, ((TicketTaker*)replyTo)->myName, numSubs+1);
	msgInt = numSubs+1;
	((TicketTaker*)replyTo)->SendMessage("HereAreTix", 3, this, tickets, &msgInt);
	state = "GaveTix";
}

void Customer::GaveTix()
{
	msgLock->Acquire();
	IterList* yesMsg = msgList->Find("GoOnIn");
	if(yesMsg)
	{
		msgsToSend = numSubs;
		printf("Customer %s in Group %d is leaving %s to enter theater\n", myName, groupNumber, ((TicketTaker*)replyTo)->myName);
		state = "TellSubsGoIn";
		msgLock->Release();
	}else{
		IterList* noMsg = msgList->Find("GoToLobby");
		if(noMsg)
		{
			printf("Customer %s in Group %d is leaving %s to wait in the lobby\n", myName, groupNumber, ((TicketClerk*)replyTo)->myName);
			replyTo = noMsg->Get(0);
			state = "GoToLobby";
			msgLock->Release();
		}else{
			msgLock->Release();
			
			sleepCon->Wait(msgLock);
		}
	}
}

void Customer::TellSubsGoIn()
{
	if(msgsToSend == 0)
	{
		state = "LeadGoIn";
		((TicketTaker*)replyTo)->SendMessage("ImGoingIn",0);
	}else
	{
		((Customer*)subs->Get(msgsToSend-1))->SendMessage("GoOnIn",1,replyTo);
		msgsToSend = msgsToSend - 1;
	}
}

void Customer::LeadGoIn()
{
	cinema->theater->seatingLock->Acquire();
	
	///////////////////////////
	//SEAT CHOOSING ALGORITHM//
	///////////////////////////
	
	int OpenSeatsInRow[NUM_ROWS];//MAX_ROWS];
	for(int i=0;i<NUM_ROWS;i++)
	{
		OpenSeatsInRow[i]=0;
	}
	for(int i=0;i<NUM_ROWS;i++)
	{
		for(int j=0;j<NUM_COLS;j++)
		{
			if(!cinema->theater->seating[i*NUM_COLS+j])
			{
				OpenSeatsInRow[i]++;
			}
		}
	}
	
	
	int seated = 0;
	//Now I know how many open seats there are in each row
	//If there is a row with enough seats to fit the entire group, seat the group in that row
	for(int i=0;i<NUM_ROWS;i++)
	{
		if(OpenSeatsInRow[i]>=numSubs+1)
		{
			int startingSeat=i*NUM_COLS+(NUM_COLS-OpenSeatsInRow[i]);
			cinema->theater->seating[startingSeat]=this;
	
			myRow=i;
			myCol=NUM_COLS-OpenSeatsInRow[i];
			for(int j=1;j<numSubs+1;j++)
			{
				int* r = new int;
				*r = (startingSeat+j) / NUM_COLS;
				int* c = new int;
				*c = (startingSeat+j) % NUM_COLS;
				printf("%s is putting %s in seat %d-%d\n",myName,((Customer *)subs->Get(j -1))->myName,*r,*c);
				((Customer *)subs->Get(j -1))->SendMessage("YouAreInSeat",3,this,r,c);
				cinema->theater->seating[startingSeat+j] = ((Customer *)subs->Get(j -1));
			}
			seated=1;
			break;
		}
	}
	//If there is not space in a single row, then check if there is space in two adjacent rows
	if(!seated)
	{
		for(int i=0;i<NUM_ROWS-1;i++)
		{
			int startingSeat = 0;
			int numTakenInRow=0;
			if(OpenSeatsInRow[i]+OpenSeatsInRow[i+1] >= numSubs+1)
			{
				startingSeat=i*NUM_COLS+(NUM_COLS-OpenSeatsInRow[i]);
				getCineplex()->theater->seating[startingSeat]=this;
				myRow=i;
				myCol=NUM_COLS-OpenSeatsInRow[i];
				int j=0;
				for(j=1;OpenSeatsInRow[i]-j>0;j++)
				{
					int* r = new int;
					*r = (startingSeat+j) / NUM_COLS;
					int* c = new int;
					*c = (startingSeat+j) % NUM_COLS;
					((Customer *)subs->Get(j -1))->SendMessage("YouAreInSeat",3,this,r,c);
					printf("%s is putting %s in seat %d-%d\n",myName,((Customer *)subs->Get(j -1))->myName,*r,*c);
					cinema->theater->seating[startingSeat+j] = ((Customer *)subs->Get(j -1));
				}
				i++;
				//Sat as many as possible in the first row, and now will put people in the next row
				startingSeat=(i)*NUM_COLS+(NUM_COLS-OpenSeatsInRow[i]);
				
				for(j=j;j<numSubs+1;j++)
				{
					int* r = new int;
					*r = (startingSeat+numTakenInRow) / NUM_COLS;
					int* c = new int;
					*c = (startingSeat+numTakenInRow) % NUM_COLS;
					((Customer *)subs->Get(j -1))->SendMessage("YouAreInSeat",3,this,r,c);
					printf("%s is putting %s in seat %d-%d\n",myName,((Customer *)subs->Get(j -1))->myName,*r,*c);
					cinema->theater->seating[startingSeat+numTakenInRow] = ((Customer *)subs->Get(j -1));
					numTakenInRow++;
				}
				seated=1;
				break;
			}
		}
	}
	
	//If there weren't any adjacent rows with enough seats for the group, just find the first they can
	if(!seated)
	{
		printf("no adjacent rows\n\n");
		int seatsAssigned = 0;
		for(int i=0;i<NUM_ROWS;i++)
		{
			for(int j=0;j<NUM_COLS;j++)
			{
				Customer* cust=((Customer *)cinema->theater->seating[i*NUM_COLS + j]);
				if((!(cust)) && seatsAssigned < (numSubs + 1))
				{
					if(!seatsAssigned) //seat lead
					{
						cinema->theater->seating[i*NUM_COLS + j] = this;
						myRow = i;
						myCol = j;
						seatsAssigned = seatsAssigned + 1;
					}
					else
					{
						int* r = new int; 
						*r = i;
						int* c = new int;
						*c = j;
						((Customer *)subs->Get(seatsAssigned-1))->SendMessage("YouAreInSeat",3,this,r,c);
						printf("%s is putting %s in seat %d-%d\n",myName,((Customer *)subs->Get(seatsAssigned-1))->myName,*r,*c);
						cinema->theater->seating[i*NUM_COLS + j] = ((Customer *)subs->Get(seatsAssigned-1));
						seatsAssigned = seatsAssigned + 1;
					}
				}
			}
			if(seatsAssigned == (numSubs + 1)) break;
		}
		seated = 1;
	}
	
	
	/**/
	if(!cinema->runSchedulers) //print seating chart if running tests
	{
		printf("Customer %s with group size %d seated now, printing seating chart:\n", myName, numSubs+1);
		for(int i=0;i<NUM_ROWS;i++)
		{
			for(int j=0;j<NUM_COLS;j++)
			{
				if((Customer *)getCineplex()->theater->seating[i*NUM_COLS+j])
				{
					printf("| G-%-2d |", ((Customer *)(getCineplex()->theater->seating[i*NUM_COLS+j]))->groupNumber);
				}
				else
				{
					printf("| ____ |");
				}
			}
			printf("\n");
		}
	}
	
	cinema->theater->seatingLock->Release();
	//msgsToSend = numSubs;
	//repliesWaiting = numSubs;
	printf("Customer %s in Group %d is sitting in seat %d-%d\n", myName, groupNumber, myRow, myCol);
	state = "WatchingMovie";
}

void Customer::WatchingMovie()
{
 	msgLock->Acquire();
	IterList* leaveMsg = msgList->Find("MovieIsDone");
	if(leaveMsg)
	{
		repliesWaiting = numSubs;
		printf("Customer %s in Group %d is leaving seat %d-%d\n", myName, groupNumber, myRow, myCol);
		state = "LeadRegroup";
		msgLock->Release();
	}else{
		msgLock->Release();
		
		sleepCon->Wait(msgLock);
	}
}

void Customer::LeadRegroup()
{
	if(repliesWaiting == 0)
	{
		cinema->theater->seatingLock->Acquire();
		cinema->theater->seating[myRow*NUM_COLS + myCol] = NULL;
		cinema->theater->seatsFilled = cinema->theater->seatsFilled - 1;
		printf("Theater->SeatsFilled = %d\n",cinema->theater->seatsFilled);
		myRow = -1;
		myCol = -1;
		cinema->theater->seatingLock->Release();
		msgsToSend = numSubs;
		state = "AskUseBR";
	}else{
		msgLock->Acquire();
		IterList* brMsg = msgList->Find("HaveRegrouped");
		if(brMsg)
		{
			repliesWaiting = repliesWaiting - 1;
			msgLock->Release();
		}else{
			msgLock->Release();
			
			sleepCon->Wait(msgLock);
		}
	}
}

void Customer::AskUseBR()
{
	if(msgsToSend == 0)
	{
		repliesWaiting = numSubs;
		state = "FindOutBR";
	}else{
		((Customer*)subs->Get(msgsToSend-1))->SendMessage("DoYouNeedBR",1,this);
		msgsToSend = msgsToSend - 1;
	}
}

void Customer::FindOutBR()
{
	if(repliesWaiting == 0)
	{
		state = "WaitBR";
	}else{
		msgLock->Acquire();
		IterList* brMsg = msgList->Find("NeedBR");
		if(brMsg)
		{
			if(brMsg->GetInt(0)) brQueue->Append(brMsg->Get(0));
			repliesWaiting = repliesWaiting - 1;
			msgLock->Release();
		}else{
			msgLock->Release();
			
			sleepCon->Wait(msgLock);
		}
	}
}

void Customer::WaitBR()
{
	if(brQueue->getSize() == 0)
	{
		msgsToSend = numSubs;
		state = "LeaveCineplex";
	}
	else
	{
		msgLock->Acquire();
		IterList* brMsg = msgList->Find("BackFromBR");
		if(brMsg)
		{
			brQueue->Pop(0); //doesn't really matter which gets popped
			msgLock->Release();
		}else{
			msgLock->Release();
			
			sleepCon->Wait(msgLock);
		}
	}
}

void Customer::LeaveCineplex()
{
	if(msgsToSend == 0)
	{
		printf("Customer %s in Group %d has left the movie theater\n", myName, groupNumber);
		state = "Completed";
	}else{
		((Customer*)subs->Get(msgsToSend-1))->SendMessage("LeaveCineplex",0);
		msgsToSend = msgsToSend - 1;
	}
}

void Customer::GoToLobby()
{
	((Lobby*)replyTo)->SendMessage("IAmInLobby", 1, this);
	printf("Customer %s has lead Group %d to the Lobby\n", myName, groupNumber);
	state = "InLobby";
}

void Customer::InLobby()
{
	msgLock->Acquire();
	IterList* lobbyMsg = msgList->Find("HowManyInYourGroup");
	if(lobbyMsg)
	{
		state = "RecSizeRequest";
		msgLock->Release();
	}else{
		IterList* enterMsg = msgList->Find("LeaveLobby");
		if(enterMsg)
		{
			printf("Customer %s is leading Group %d out of the Lobby\n", myName, groupNumber);
			state = "JoinEntryLine";
			msgLock->Release();
		}else{
			msgLock->Release();
			
			sleepCon->Wait(msgLock);
		}
	}
}

void Customer::RecSizeRequest()
{
	msgInt = numSubs+1;
	((Lobby*)replyTo)->SendMessage("WeAreLegion", 2, this, &msgInt);
	state = "InLobby";
}

//SUB ACTION

void Customer::sNewSub()
{
	msgLock->Acquire();
	IterList* foodMsg = msgList->Find("WouldYouLikeFood");
	if(foodMsg)
	{
		replyTo = foodMsg->Get(0);
		state = "sSendFoodOrder";
		msgLock->Release();
	}else{
		msgLock->Release();
		
		sleepCon->Wait(msgLock);
	}
}

void Customer::sSendFoodOrder()
{
	numPopcorn = ((getRandPercent() < 75) ? 1 : 0); //decide own popcorn
	numSoda = ((getRandPercent() < 75) ? 1 : 0); //decide own soda
	((Customer*)replyTo)->SendMessage("IWouldLike",2,&numPopcorn,&numSoda);
	printf("Customer %s in group %d wants %d popcorn and %d soda.\n", myName, groupNumber, numPopcorn, numSoda);
	state = "sFoodOrderSent";
}

void Customer::sFoodOrderSent()
{
	msgLock->Acquire();
	IterList* enterMsg = msgList->Find("GoOnIn");
	if(enterMsg)
	{
		replyTo = enterMsg->Get(0);
		state = "sRecGoIn";
		msgLock->Release();
	}else{
		msgLock->Release();
		
		sleepCon->Wait(msgLock);
	}
}

void Customer::sRecGoIn()
{
	((TicketTaker*)replyTo)->SendMessage("ImGoingIn",0);
	printf("Customer %s going in to sit\n",myName);
	state = "sWaitForSeat";
}

void Customer::sWaitForSeat()
{
	msgLock->Acquire();
	IterList* seatMsg = msgList->Find("YouAreInSeat");
	if(seatMsg)
	{
		replyTo = seatMsg->Get(0);
		myRow = seatMsg->GetInt(1);
		myCol = seatMsg->GetInt(2);
		//printf("Sub Customer %s in Group %d is sitting in seat %d-%d\n", myName, groupNumber, myRow, myCol);
		state = "sWatchingMovie";
		msgLock->Release();
	}else{
		msgLock->Release();
		
		sleepCon->Wait(msgLock);
	}
}

void Customer::sWatchingMovie()
{
	msgLock->Acquire();
	IterList* seatMsg = msgList->Find("MovieIsDone");
	if(seatMsg)
	{
		printf("Sub Customer %s in Group %d is leaving seat %d-%d\n", myName, groupNumber, myRow, myCol);
		state = "sRegroup";
		msgLock->Release();
	}else{
		msgLock->Release();
		
		sleepCon->Wait(msgLock);
	}
}

void Customer::sRegroup()
{
	cinema->theater->seatingLock->Acquire();
	cinema->theater->seating[myRow*NUM_COLS + myCol] = NULL;
	cinema->theater->seatsFilled = cinema->theater->seatsFilled - 1;
	myRow = -1;
	myCol = -1;
	printf("Theater->SeatsFilled = %d\n",cinema->theater->seatsFilled);
	cinema->theater->seatingLock->Release();
	//printf("Sub Customer %s in Group %d is regrouping\n", myName, groupNumber);
	((Customer*)replyTo)->SendMessage("HaveRegrouped",0);
	
	state = "sLeaveTheater";
}
//waiting to be asked about BR
void Customer::sLeaveTheater()
{
	msgLock->Acquire();
	IterList* brMsg = msgList->Find("DoYouNeedBR");
	if(brMsg)
	{
		state = "sAskedBR";
		msgLock->Release();
	}else{
		msgLock->Release();
		
		sleepCon->Wait(msgLock);
	}
}

void Customer::sAskedBR()
{
	if(getRandPercent() < 25)
	{
		msgInt = 1;
		((Customer*)replyTo)->SendMessage("NeedBR",1,&msgInt); //NEED
		state = "sUseBR";
	}else{
		msgInt = 0;
		((Customer*)replyTo)->SendMessage("NeedBR",1,&msgInt); //DON'T NEED
		state = "sWaitBR";
	}
}

void Customer::sUseBR()
{
	cinema->bathroom->brLock->Acquire();
	printf("Customer %s in Group %d is using the bathroom\n", myName, groupNumber);
	for(int i = 0; i < getRandPercent(); i++)
	{
		//currentThread->Yield();
	}
	cinema->bathroom->brLock->Release();
	printf("Customer %s in Group %d is leaving the bathroom\n", myName, groupNumber);
	
	((Customer*)replyTo)->SendMessage("BackFromBR",0);
	state = "sWaitBR";

}

void Customer::sWaitBR()
{
	msgLock->Acquire();
	IterList* leaveMsg = msgList->Find("LeaveCineplex");
	if(leaveMsg)
	{
		state = "sLeaveCineplex";
		msgLock->Release();
	}else{
		msgLock->Release();
		
		sleepCon->Wait(msgLock);
	}
}

void Customer::sLeaveCineplex()
{
	printf("Customer %s in Group %d has left the movie theater\n", myName, groupNumber);
	state = "Completed";
}
